﻿//////////////////////////////////////////////////////////////////////////////
//
//  Copyright © 1998-2024 Glodon Company Limited.  All rights reserved.
//
//  Use of this software is subject to the terms of the Glodon license
//  agreement provided at the time of installation or download, or which
//  otherwise accompanies this software in either electronic or hard copy form.  
//
//////////////////////////////////////////////////////////////////////////////

#pragma once

#include "GcmpModelInterface.h"
#include "IDbObject.h"
#include "NdbDataSchema.h"

namespace gcmp {

    class IDocument;
    class IParameter;
    class IElement;
    class IElementParentReporter;
    class ICopyContext;
    class ICopyStrategyReporter;
    class ICalculatorCollection;

    /// \brief 外部数据扩展接口
    ///
    /// 提供额外存储数据的地方，用户如需扩展内部内，如IInstance、IModelView等，可通过该接口增加数据；
    /// 通过该接口添加的数据，通过报告计算器，可参与关联更新计算；
    /// 通过该接口添加的数据，可以随着Element一同被拷贝，需要用户正确报告拷贝策略。
    class GCMP_MODEL_INTERFACE_EXPORT IExternalData : public IDbObject
    {
        DECLARE_CLASS_WITHOUT_DATA_MEMBERS(gcmp, IExternalData, gcmp::IDbObject, AACD6A8E - E61F - 4E1B - BC04 - B1A0A32CFCB2, gcmpmodelinterface)
    public:
        /// \brief 设置该行为所属Element
        /// \param pOwnerElement 所属Element
        /// \return bool
        virtual bool SetOwnerElement(IElement* pOwnerElement) = 0;

        /// \brief 获得该外部对象所属Element
        /// \return 所属Element
        virtual const IElement* GetOwnerElement() const = 0;

        /// \brief 获得该外部对象所属Element
        /// \return 所属Element
        virtual IElement* GetOwnerElement() = 0;

#pragma region 关联更新

        /// \brief 当弱依赖的Element被删除后，此接口会被调用。
        /// \param deleteElementIds 表示将被删除的Element ids
        /// \return void
        virtual void UpdateForWeakParentDeletion(const std::set<ElementId>& deletedElementIds) = 0;

        /// \brief 报告依赖的ElementIds。对象删除和关联更新依赖于这个接口的正确实现。
        /// \param reporter 报告接口
        /// \return void
        virtual void ReportParents(IElementParentReporter& reporter) const = 0;

        /// \brief 外部用户报告自己定制的计算器
        ///
        /// GetCalculators中需要调用ICalculatorCollection::AddCalculator(OwnerPtr<ICalculator>)将计算器注册到ICalculatorCollection中。
        /// 若配合ADD_CALCULATOR宏使用，用户重写此方法时形参名也需使用“calculators”。
        /// \param calculators 计算器收集器指针
        /// \return void
        virtual void GetCalculators(ICalculatorCollection* calculators) const = 0;
#pragma endregion 关联更新

#pragma region 参数

        /// \brief 报告原生参数
        /// \param pParameterIds Element拥有的原生参数
        /// \return void
        virtual void ReportParameterDefinitions(std::vector<int>* pParameterIds) const = 0;

        /// \brief 通过参数id来获得原生参数，此参数id文档唯一。
        /// \param parameterId 文档唯一的参数id
        /// \return 返回Parameter对象，如果失败则返回空的OwnerPtr。
        virtual OwnerPtr<IParameter> GetNativeParameter(int parameterId) const = 0;

        /// \brief 设置原生参数
        /// 如果Element有原生参数（Native Parameter），该类需定制SetNativeParameter方法。
        /// \param pParameter 输入要设置的参数对象
        /// \param errorMessage 输出错误信息，为可选参数。失败的情况下获取错误信息。
        /// \return bool 返回true表示设置成功，否则表示设置失败。如果失败可以通过errorMessage参数获取错误信息。
        virtual bool SetNativeParameter(const IParameter *pParameter, std::wstring* errorMsg = nullptr) = 0;

        /// \brief 获取参数是否可以被修改
        /// \param parameterId 文档唯一的参数id
        /// \return 如果参数可被修改，返回true。否则返回false。
        virtual bool IsParameterModifiable(int parameterId) const = 0;
#pragma endregion 参数

#pragma region 复制粘贴
        /// \brief 报告引用的Element的id为拷贝、保持、放弃
        /// \param copyContext 拷贝上下文
        /// \param reporter 拷贝策略报告器
        /// \return void
        /// \see ICopyStrategyReporter
        virtual void Report(const gcmp::ICopyContext & copyContext, gcmp::ICopyStrategyReporter& reporter) const = 0;

        /// \brief 复制粘贴后更新操作
        ///
        /// 复制粘贴后需要的后处理可以放在这里，比如：对被复制粘贴后的元素进行重新命名
        ///
        /// \return void
        virtual void UpdateAfterCopy(const ICopyContext& copyContext) = 0;

        /// \brief 当Weak Parent被拷贝但此Element没有同时被拷贝时，此函数被调用
        ///
        /// 调用时机：当Weak Parent被拷贝但此Element没有同时被拷贝时，此Element会通过此接口收到通知。 
        /// 用途：一般用来处理Element存在Id容器的情况。 
        /// 例子：当Sketch里的对象被拷贝但Sketch本身没被拷贝时，Sketch通过实现此接口将拷贝的新对象加到SketchObjectId容器中。 
        ///
        /// \param pDocumentFrom 源文档
        /// \param pSource 被拷贝的IElement指针
        /// \param pCopy 拷贝得到的IElement指针
        /// \return void
        virtual void UpdateForWeakParentCopied(
            const IDocument* pDocumentFrom,
            const IElement* pSource,
            const IElement* pCopy
        ) const = 0;

        /// \brief 决定是否需要一起被拷贝
        ///
        /// 调用时机：当一个对象是否拷贝不仅仅取决于一个对象时，无法通过Report来直接决定是否拷贝。 
        /// 用途：根据待拷贝对象集来决定自己是否拷贝 
        /// 例子：墙连接取决于连接的两个墙、尺寸约束取决于约束的对象 
        ///
        /// \param copyContext 拷贝上下文
        /// \return 返回true表示需要，false表示不需要。
        virtual bool ShouldBeCopiedTogether(const ICopyContext & copyContext) const = 0;

        /// \brief 跨文档拷贝时判断StrongParents是否需要被拷贝
        ///
        /// 调用时机：跨文档拷贝时判断StrongParents是否需要被拷贝。 
        /// \return 返回true表示需要，false表示不需要。
        virtual bool IsStrongParentsNeedCopied(void) const = 0;

        /// \brief 判断是否可以复制
        /// \param pErrorMessage 输出信息
        /// \return 返回true表示可以复制，false表示不能复制。
        virtual bool CanBeCopied(std::wstring* pErrorMessage) const = 0;

#pragma endregion 复制粘贴

#pragma region 通用操作
        /// \brief 对象的转换操作。
        /// \param matrix 转换矩阵。
        /// \return 返回true表示成功，false表示失败。
        virtual bool Transform(const Matrix4d& matrix) = 0;
#pragma endregion 通用操作

    };

}// namespace gcmp


